package com.xuwangcheng.springboot.common.base.service.impl;

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.enums.SqlMethod;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.baomidou.mybatisplus.core.toolkit.GlobalConfigUtils;
import com.baomidou.mybatisplus.core.toolkit.ReflectionKit;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
import com.xuwangcheng.springboot.common.base.dao.BaseDao;
import com.xuwangcheng.springboot.common.base.dto.BaseQueryInDTO;
import com.xuwangcheng.springboot.common.base.page.PageData;
import com.xuwangcheng.springboot.common.base.service.BaseService;
import com.xuwangcheng.springboot.common.constant.SystemConstant;
import org.apache.ibatis.binding.MapperMethod;
import org.apache.ibatis.session.SqlSession;
import org.mybatis.spring.SqlSessionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

import java.io.Serializable;
import java.util.Collection;
import java.util.List;

/**
 * 基础Service实现
 * @author xuwangcheng
 * @version 1.0.0
 * @description
 * @date 2020/4/29 9:08
 */
public abstract class BaseServiceImpl<M extends BaseDao<T, D>, T, D> implements BaseService<T, D> {
    @Autowired
    protected M baseDao;

    @Override
    public PageData<D> queryDTOPage(BaseQueryInDTO params) {
        Page<D> list = (Page<D>) this.baseDao.selectPage(getPage(params), params);
        PageData<D> pageData = new PageData<>(wrapDTOObject(list.getRecords()), list.getTotal());

        return pageData;
    }

    @Override
    public List<D> listDTO(BaseQueryInDTO params) {
        params.setLimit(9999999999L);
        Page page = getPage(params);
        page.setSearchCount(false);
        page.setOptimizeCountSql(false);

        Page<D> list = (Page<D>) this.baseDao.selectPage(page, params);

        return wrapDTOObject(list.getRecords());
    }

    /**
     * 获取简单分页对象
     * @author xuwangcheng
     * @date 2019/6/4 15:01
     * @param params params
     * @return {@link Page}
     */
    public Page getPage(BaseQueryInDTO params) {
        Page page = new Page<>();
        page.setSize(params.getLimit());
        page.setCurrent(params.getPage());

        //设置排序
        if (StrUtil.isNotBlank(params.getOrder()) && StrUtil.isNotBlank(params.getOrderField())) {
            if (params.getOrder().equalsIgnoreCase(SystemConstant.ASC)) {
                page.setAsc(params.getOrderField());
            } else {
                page.setDesc(params.getOrderField());
            }
            return page;
        }

        //设置默认排序
        setDefaultOrderFields(page);

        return page;
    }

    /**
     * 获取自定义VO对象时自定义处理返回的dto对象
     * @author xuwangcheng
     * @date 2019/6/6 9:33
     * @param dto dto
     * @return {@link List}
     */
    public List<D> wrapDTOObject(List<D> dto) {
        return dto;
    }

    /**
     * 获取自定义VO对象时设置默认的排序字段
     * @author xuwangcheng
     * @date 2019/6/4 15:01
     * @param page page
     */
    public abstract void setDefaultOrderFields (Page page);

    /**
     * <p>
     * 判断数据库操作是否成功
     * </p>
     * <p>
     * 注意！！ 该方法为 Integer 判断，不可传入 int 基本类型
     * </p>
     *
     * @param result 数据库操作返回影响条数
     * @return boolean
     */
    protected static boolean retBool(Integer result) {
        return SqlHelper.retBool(result);
    }

    protected Class<T> currentModelClass() {
        return ReflectionKit.getSuperClassGenericType(getClass(), 1);
    }

    /**
     * <p>
     * 批量操作 SqlSession
     * </p>
     */
    protected SqlSession sqlSessionBatch() {
        return SqlHelper.sqlSessionBatch(currentModelClass());
    }

    /**
     * 释放sqlSession
     * @param sqlSession session
     */
    protected void closeSqlSession(SqlSession sqlSession){
        SqlSessionUtils.closeSqlSession(sqlSession, GlobalConfigUtils.currentSessionFactory(currentModelClass()));
    }

    /**
     * 获取SqlStatement
     *
     * @param sqlMethod
     * @return
     */
    protected String sqlStatement(SqlMethod sqlMethod) {
        return SqlHelper.table(currentModelClass()).getSqlStatement(sqlMethod.getMethod());
    }

    @Override
    public boolean insert(T entity) {
        return BaseServiceImpl.retBool(baseDao.insert(entity));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean insertBatch(Collection<T> entityList) {
        return insertBatch(entityList, 100);
    }

    /**
     * 批量插入
     *
     * @param entityList
     * @param batchSize
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean insertBatch(Collection<T> entityList, int batchSize) {
        SqlSession batchSqlSession = sqlSessionBatch();
        int i = 0;
        String sqlStatement = sqlStatement(SqlMethod.INSERT_ONE);
        try {
            for (T anEntityList : entityList) {
                batchSqlSession.insert(sqlStatement, anEntityList);
                if (i >= 1 && i % batchSize == 0) {
                    batchSqlSession.flushStatements();
                }
                i++;
            }
            batchSqlSession.flushStatements();
        }finally {
            closeSqlSession(batchSqlSession);
        }
        return true;
    }

    @Override
    public boolean updateById(T entity) {
        return BaseServiceImpl.retBool(baseDao.updateById(entity));
    }

    @Override
    public boolean update(T entity, Wrapper<T> updateWrapper) {
        return BaseServiceImpl.retBool(baseDao.update(entity, updateWrapper));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateBatchById(Collection<T> entityList) {
        return updateBatchById(entityList, 30);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateBatchById(Collection<T> entityList, int batchSize) {
        if (CollectionUtils.isEmpty(entityList)) {
            throw new IllegalArgumentException("Error: entityList must not be empty");
        }
        SqlSession batchSqlSession = sqlSessionBatch();
        int i = 0;
        String sqlStatement = sqlStatement(SqlMethod.UPDATE_BY_ID);
        try {
            for (T anEntityList : entityList) {
                MapperMethod.ParamMap<T> param = new MapperMethod.ParamMap<>();
                param.put(Constants.ENTITY, anEntityList);
                batchSqlSession.update(sqlStatement, param);
                if (i >= 1 && i % batchSize == 0) {
                    batchSqlSession.flushStatements();
                }
                i++;
            }
            batchSqlSession.flushStatements();
        }finally {
            closeSqlSession(batchSqlSession);
        }
        return true;
    }

    @Override
    public T selectById(Serializable id) {
        return baseDao.selectById(id);
    }

    @Override
    public boolean deleteById(Serializable id) {
        return SqlHelper.delBool(baseDao.deleteById(id));
    }

    @Override
    public boolean deleteBatchIds(Collection<? extends Serializable> idList) {
        return SqlHelper.delBool(baseDao.deleteBatchIds(idList));
    }

    @Override
    public boolean deleteByWrapper(Wrapper wrapper) {
        return SqlHelper.delBool(baseDao.delete(wrapper));
    }

    @Override
    public List<T> list(Wrapper wrapper) {
        return baseDao.selectList(wrapper);
    }

    @Override
    public Integer selectCount(Wrapper wrapper) {
        return baseDao.selectCount(wrapper);
    }

    @Override
    public T selectOne(Wrapper<T> wrapper) {
        return baseDao.selectOne(wrapper) ;
    }

    @Override
    public List<Object> selectObjs(Wrapper<T> wrapper) {
        return baseDao.selectObjs(wrapper);
    }
}
